#ifndef _QGCM_DIAGFUNC_HEADERS_
#define _QGCM_DIAGFUNC_HEADERS_
#include <mpParser.h>
#include <cstdlib>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <vector>
#include "qgcmio.h"
using namespace std;
using namespace mup;

class model_class; // forward declaration

const EPackages pckNONE = static_cast<EPackages>( 0 );

/* Global array indexing function */
Variable * diagOprtArrayIndex(const array_type &, const IValue & );

/* Foreign interface for handlers */
string gethandleinfo( const IValue & );

/* typedefinitions: */
class diag_func_class;
#define diag_call_stack const diag_func_class & func_class, model_class & mo, bool compiled, ptr_val_type & ret, const ptr_val_type * argv, const int argc
#define diag_builtin_call_stack ptr_val_type & ret, const ptr_val_type * argv, const int argc
#define diag_stack func_class,compiled,ret,argv,argc
#define diag_bin_call_stack ptr_val_type& ret, const IValue * arg1ptr, const IValue * arg2ptr
#define diag_infix_call_stack ptr_val_type & ret, const ptr_val_type & arg
#define diag_assign_call_stack Variable & dest, const ptr_val_type & lhs 
#define diag_self_assign_call_stack Variable & dest

typedef void (*diag_func)(diag_call_stack);
typedef void (*diag_builtin_func)(diag_builtin_call_stack);
typedef void (*diag_bin_oprt_func)(diag_bin_call_stack);
typedef void (*diag_assign_oprt_func)(diag_assign_call_stack);
typedef void (*diag_infix_oprt_func)(diag_infix_call_stack);
typedef void (*diag_self_assign_oprt_func)(diag_self_assign_call_stack);

// ................................................................................ //
/* global scope functions : */

void parser_define_basics(ParserX&p, model_class *, const char * types, bool optimized, bool diag, int pindex = -1);

bool is_value_valid_handle(const IValue & val);
bool does_value_conform_to_type(const IValue & val, char_type t);
bool does_type_conform_to_type(char_type , char_type );

void set_default_value_for_type(IValue & val);
bool is_string_var_type(const string &);
char_type get_var_type_from_string(const string &);
bool cast_to_bool(const IValue &);
char cast_to_char(const IValue &);
int cast_to_int(const IValue &);
float cast_to_float(const IValue &);
string cast_to_string(const IValue &);
const IValue & get_default_value_of_type(char_type);

bool is_type_valid(char_type);

void create_function_call_stack_string(const vector<char_type> & stacklist, string & dest);

int mpError(const char * fmt, ...);
void set_mp_errmsg(const char * fmt, ...);
const string & get_mp_errmsg();
// ................................................................................ //
/* class interface for diagnostic functions: */
class diag_func_class : public ICallback {
  string name;
  string descr;
  string func_sig;
  bool variadic; // can this function take variable number of arguments
  bool optimized; 
  diag_func func_ptr; 
  private: // private CI
  void GenDesc();
  public:
  diag_func_class(diag_func, const char * name, const string & call_sig, bool opt);
  virtual void Eval(ptr_val_type & ret, const ptr_val_type *, int);
  virtual const char_type * GetProtoType() const;
  virtual const char_type * GetDesc() const;
  virtual IToken * Clone() const;
  const string & GetName() const;
};
/* end class interface diagnosti functions */
// ................................................................................ //
/* class operators of type a=b (binary) supports overloaded types */
class diagAssignOprt : public IOprtBin 
{
  model_class * mo;
  string descr;
  bool optimized;
  string func_sig;
  diag_assign_oprt_func callback;
  diagAssignOprt();
  void GenDesc();
  public:
  diagAssignOprt(model_class *, const char * oprt_id, EOprtPrecedence priority, EOprtAsct assoc,
      pair< string, diag_assign_oprt_func> &, bool optimize);
  virtual void Eval(ptr_val_type & ret, const ptr_val_type *a_pArg, int);
  virtual const char_type * GetProtoType() const;
  virtual const char_type * GetDesc() const;
  virtual IToken * Clone() const;
};
// ................................................................................ //
/* class operators of type ++a, --a;  that self assign a value to their argument */
class diagSelfAssignOprt : public IOprtInfix 
{
  model_class * mo;
  string descr;
  bool optimized;
  string func_sig;
  diag_self_assign_oprt_func callback;
  diagSelfAssignOprt();
  void GenDesc();
  public:
  diagSelfAssignOprt(model_class *, const char * oprt_id, pair< string, diag_self_assign_oprt_func> &, bool optimize); 
  virtual void Eval(ptr_val_type & ret, const ptr_val_type *a_pArg, int);
  virtual const char_type * GetProtoType() const;
  virtual const char_type * GetDesc() const;
  virtual IToken * Clone() const;
};
// ................................................................................ //
/* class inFixOprt : user defined inFixOprt */
class diagInfixOprt : public IOprtInfix 
{
  model_class * mo;
  string descr;
  string func_sig;
  diag_infix_oprt_func callback;
  diagInfixOprt();
  void GenDesc();
  public:
  diagInfixOprt(model_class *, const char * oprt_id, 
      pair< string, diag_infix_oprt_func>  &, bool optimize); 
  virtual void Eval(ptr_val_type & ret, const ptr_val_type *a_pArg, int);
  virtual const char_type * GetProtoType() const;
  virtual const char_type * GetDesc() const;
  virtual IToken * Clone() const;
};
// ................................................................................ //
/* class BinOprt : user defined binary operators */
class diagOprtBin : public IOprtBin
{
  model_class * mo;
  string descr;
  string func_sig;
  diag_bin_oprt_func callback; // do not check type, just call this function
  diagOprtBin(); // no default constructor
  void GenDesc();
  public:
  diagOprtBin(
      model_class * model, const char * token_str, EOprtPrecedence priority, EOprtAsct assoc,
      pair< string, diag_bin_oprt_func> & cstack, bool optimize
      );
  virtual const char_type * GetDesc() const;
  virtual const char_type * GetProtoType() const;
  virtual void Eval(ptr_val_type & ret, const ptr_val_type *, int);
  virtual IToken * Clone() const;
};
// ................................................................................ //
/* class profileBinOprt : user defined binary operators for handle profiles and data assignments */
class profileOprtBin : public IOprtBin
{
  model_class * mo;
  const int pindex;
  string descr;
  string func_sig;
  diag_bin_oprt_func callback;
  profileOprtBin(); // no default constructor
  void GenDesc();
  public:
  profileOprtBin(
      model_class * model, const char * token_str, EOprtPrecedence priority, EOprtAsct assoc,
      pair< string, diag_bin_oprt_func> & cstack, 
      const int index 
      );
  virtual const char_type * GetDesc() const;
  virtual const char_type * GetProtoType() const;
  virtual void Eval(ptr_val_type & ret, const ptr_val_type *, int);
  virtual IToken * Clone() const;
};
// ................................................................................ //
class diagBuiltInFunc : public ICallback
{
  model_class * mo;
  string descr;
  char_type ret_type;
  diag_builtin_func callback; // ptr to function to call
  string func_sig;
  diagBuiltInFunc();
  void GenDesc();
  public:
  diagBuiltInFunc(model_class * model, const char * name,
      pair<string, diag_builtin_func> & arglist, 
      bool optimized);
  virtual void Eval(ptr_val_type & ret, const ptr_val_type * argv, int argc);
  const char_type * GetDesc() const;
  virtual const char_type * GetProtoType() const;
  IToken * Clone() const;
};
// ................................................................................ //
/* class interface handle heap organizer: */
class handle_collection {
  int_type stdout_id;
  map< int_type, qstream *> file_handles;
  nchandler ncfile_handles;
  model_class * mo;
  public:
  handle_collection();
  private:
  handle_collection(const handle_collection &);
  handle_collection & operator=(const handle_collection &); 
  int_type add_stream(qstream *);
  public:
  const qstream * get_file(const IValue & handle) const;
  NcFile * get_nc_file(const IValue & handle);
  bool is_handle_valid(const IValue & handle);
  Value file_open(const char * name, const char * fmt);
  Value ncfile_open(const char * name, NcFile::FileMode);
  bool file_close(const IValue &);
  bool ncfile_close(const IValue &);
  const Value getstdout() const;
};
/* end class interface handle heap organizer: */
// ................................................................................ //
/* list of build in functions assigned to parsers by default */
void diag_builtin_abs_complex(diag_builtin_call_stack);
void diag_builtin_abs_int(diag_builtin_call_stack);
void diag_builtin_abs_real(diag_builtin_call_stack);

void diag_builtin_sin_real(diag_builtin_call_stack);
void diag_builtin_cos_real(diag_builtin_call_stack);
void diag_builtin_tan_real(diag_builtin_call_stack);
void diag_builtin_asin_real(diag_builtin_call_stack);
void diag_builtin_acos_real(diag_builtin_call_stack);
void diag_builtin_atan_real(diag_builtin_call_stack);
void diag_builtin_sinh_real(diag_builtin_call_stack);
void diag_builtin_cosh_real(diag_builtin_call_stack);
void diag_builtin_tanh_real(diag_builtin_call_stack);
void diag_builtin_ln_real(diag_builtin_call_stack);
void diag_builtin_log10_real(diag_builtin_call_stack);

void diag_builtin_exp_real(diag_builtin_call_stack);
void diag_builtin_exp_complex(diag_builtin_call_stack);

void diag_builtin_sqrt_complex(diag_builtin_call_stack);
void diag_builtin_sqrt_real(diag_builtin_call_stack);

void diag_builtin_real_complex(diag_builtin_call_stack);
void diag_builtin_imag_complex(diag_builtin_call_stack);
void diag_builtin_conj_complex(diag_builtin_call_stack);

void diag_builtin_min_int(diag_builtin_call_stack);
void diag_builtin_min_real(diag_builtin_call_stack);
void diag_builtin_max_int(diag_builtin_call_stack);
void diag_builtin_max_real(diag_builtin_call_stack);

void diag_builtin_sum_int(diag_builtin_call_stack);
void diag_builtin_sum_real(diag_builtin_call_stack);

void diag_builtin_length_string(diag_builtin_call_stack);
void diag_builtin_length_array(diag_builtin_call_stack);

// ................................................................................ //
/* list of diag binary operator call backs */
void diag_bin_oprt_add_complex(diag_bin_call_stack);
void diag_bin_oprt_add_real(diag_bin_call_stack);
void diag_bin_oprt_add_int(diag_bin_call_stack);
void diag_bin_oprt_add_string(diag_bin_call_stack);

void diag_bin_oprt_sub_complex(diag_bin_call_stack);
void diag_bin_oprt_sub_real(diag_bin_call_stack);
void diag_bin_oprt_sub_int(diag_bin_call_stack);

void diag_bin_oprt_mul_complex(diag_bin_call_stack);
void diag_bin_oprt_mul_real(diag_bin_call_stack);
void diag_bin_oprt_mul_int(diag_bin_call_stack);

void diag_bin_oprt_div_complex(diag_bin_call_stack);
void diag_bin_oprt_div_real(diag_bin_call_stack);
void diag_bin_oprt_div_int(diag_bin_call_stack);

void diag_bin_oprt_pow_real(diag_bin_call_stack);
void diag_bin_oprt_pow_int(diag_bin_call_stack);
void diag_bin_oprt_pow_complex(diag_bin_call_stack);

void diag_bin_oprt_mod_int(diag_bin_call_stack);
void diag_bin_oprt_mod_real(diag_bin_call_stack);

void diag_bin_oprt_sharp(diag_bin_call_stack); // special array subscript operator (backwards compatibility)

// profile binary operators (allow handling of array + scalar type expressions)
void profile_bin_oprt_sharp(diag_bin_call_stack); // special array subscript operator (run on profiles only backwards compatibility)

// compare functions
void diag_bin_oprt_eq_int(diag_bin_call_stack);
void diag_bin_oprt_eq_real(diag_bin_call_stack);
void diag_bin_oprt_eq_complex(diag_bin_call_stack);
void diag_bin_oprt_eq_string(diag_bin_call_stack);
void diag_bin_oprt_eq_bool(diag_bin_call_stack);
void diag_bin_oprt_eq_array(diag_bin_call_stack);

void diag_bin_oprt_leq_int(diag_bin_call_stack);
void diag_bin_oprt_leq_real(diag_bin_call_stack);

void diag_bin_oprt_lt_int(diag_bin_call_stack);
void diag_bin_oprt_lt_real(diag_bin_call_stack);

void diag_bin_oprt_geq_int(diag_bin_call_stack);
void diag_bin_oprt_geq_real(diag_bin_call_stack);

void diag_bin_oprt_gt_int(diag_bin_call_stack);
void diag_bin_oprt_gt_real(diag_bin_call_stack);

void diag_bin_oprt_neq_int(diag_bin_call_stack);
void diag_bin_oprt_neq_real(diag_bin_call_stack);
void diag_bin_oprt_neq_complex(diag_bin_call_stack);
void diag_bin_oprt_neq_string(diag_bin_call_stack);
void diag_bin_oprt_neq_bool(diag_bin_call_stack);
void diag_bin_oprt_neq_array(diag_bin_call_stack);

// bool functions
void diag_bin_oprt_and_bool(diag_bin_call_stack);
void diag_bin_oprt_or_bool(diag_bin_call_stack);

// ................................................................................ //
/* list of diag unitary operator call backs */
void diag_uni_oprt_not_bool(diag_infix_call_stack);

void diag_uni_oprt_neg_int(diag_infix_call_stack);
void diag_uni_oprt_neg_float(diag_infix_call_stack);
void diag_uni_oprt_neg_complex(diag_infix_call_stack);

// ................................................................................ //
/* list of assignment operators */
void diag_oprt_assign_handle_2_handle(diag_assign_call_stack); 
void diag_oprt_assign_int_2_int(diag_assign_call_stack); 
void diag_oprt_assign_real_2_real(diag_assign_call_stack); 
void diag_oprt_assign_complex_2_complex(diag_assign_call_stack); 
void diag_oprt_assign_bool_2_bool(diag_assign_call_stack); 
void diag_oprt_assign_string_2_string(diag_assign_call_stack); 
void diag_oprt_assign_array_2_array(diag_assign_call_stack); 
void diag_oprt_assign_variable_2_variable(diag_assign_call_stack); 

void diag_oprt_assign_add_int(diag_assign_call_stack);
void diag_oprt_assign_add_real(diag_assign_call_stack);
void diag_oprt_assign_add_complex(diag_assign_call_stack);
void diag_oprt_assign_add_string(diag_assign_call_stack);

void diag_oprt_assign_sub_int(diag_assign_call_stack);
void diag_oprt_assign_sub_real(diag_assign_call_stack);
void diag_oprt_assign_sub_complex(diag_assign_call_stack);

void diag_oprt_assign_mul_int(diag_assign_call_stack);
void diag_oprt_assign_mul_real(diag_assign_call_stack);
void diag_oprt_assign_mul_complex(diag_assign_call_stack);

void diag_oprt_assign_div_int(diag_assign_call_stack);
void diag_oprt_assign_div_real(diag_assign_call_stack);
void diag_oprt_assign_div_complex(diag_assign_call_stack);

// ................................................................................ //
/* list of self assignment operators */
void diag_oprt_self_assign_increment_int(diag_self_assign_call_stack);
void diag_oprt_self_assign_decrement_int(diag_self_assign_call_stack);

// ................................................................................ //
/* list of diag function call backs: */
void diag_func_tindex(diag_call_stack);
void diag_func_gettime(diag_call_stack);
void diag_func_settimestep(diag_call_stack);

void diag_func_getprevtimestep(diag_call_stack);
void diag_func_gettimestep(diag_call_stack);

void diag_func_getSsysdata(diag_call_stack);
void diag_func_get_fourier_phi(diag_call_stack);
void diag_func_fclose(diag_call_stack);
void diag_func_fopen(diag_call_stack);
void diag_func_getstdout(diag_call_stack);
void diag_func_printf(diag_call_stack);
void diag_func_printf(diag_call_stack);
void diag_func_flush(diag_call_stack);
void diag_func_delete(diag_call_stack);
void diag_func_print(diag_call_stack);
void diag_func_getQ(diag_call_stack);
void diag_func_getQI(diag_call_stack);
void diag_func_getA(diag_call_stack);
void diag_func_fprintf(diag_call_stack);
void diag_func_resetdiagout(diag_call_stack);
void diag_func_system(diag_call_stack);
void diag_func_setdiagout(diag_call_stack);
void diag_func_ncopen(diag_call_stack);
void diag_func_ncsetmissingvalue(diag_call_stack);
void diag_func_ncwriteprofiles(diag_call_stack);
void diag_func_ncwritedims(diag_call_stack);
void diag_func_ncgetmaxtime(diag_call_stack);
void diag_func_ncaddtime(diag_call_stack);
void diag_func_ncwritePhi(diag_call_stack);
void diag_func_ncwriteG(diag_call_stack);
void diag_func_ncclose(diag_call_stack);
void diag_func_eval(diag_call_stack);
void diag_func_createarray(diag_call_stack);
void diag_func_arrayelement(diag_call_stack);
void diag_func_bool(diag_call_stack);
void diag_func_integer(diag_call_stack);
void diag_func_real(diag_call_stack);
void diag_func_imag(diag_call_stack);
void diag_func_complex(diag_call_stack);

void diag_func_getk(diag_call_stack);
void diag_func_getnk(diag_call_stack);
void diag_func_getnumks(diag_call_stack);
void diag_func_getdk(diag_call_stack);

void get_enum_param_type_string(diag_call_stack);
void get_enum_param_type_integer(diag_call_stack);
void get_enum_param_type_real(diag_call_stack);
void diag_func_getparamtypebyindex(diag_call_stack);
void diag_func_bessel1(diag_call_stack);
#ifdef DEBUG
void diag_func_breakpoint(diag_call_stack);
#endif
void diag_func_getparamnamebyindex(diag_call_stack);
void diag_func_getparambyindex(diag_call_stack);
void diag_func_getnumparams(diag_call_stack);
void diag_func_readenv(diag_call_stack);
void diag_func_print_model_profiles(diag_call_stack);
void diag_func_getprofilemaxsize(diag_call_stack);
void diag_func_end(diag_call_stack);
void diag_func_abort(diag_call_stack);
void diag_func_print_model_macros(diag_call_stack);
void diag_func_get_static_data(diag_call_stack);
void diag_func_rewindtime(diag_call_stack);
void diag_func_set_static_data(diag_call_stack);
void diag_func_error(diag_call_stack);
void diag_func_setmodelstdout(diag_call_stack);
void diag_func_setmodelstdmsg(diag_call_stack);
void diag_func_set_debug_io(diag_call_stack);
void diag_func_close_debug_io(diag_call_stack);
void diag_func_getnumtsteps(diag_call_stack);


void diag_func_string(diag_call_stack);

void diag_func_getmaxdigits(diag_call_stack);

void diag_func_save_model_state(diag_call_stack);

void diag_func_is_handle_valid(diag_call_stack);

#ifdef DEBUG
void diag_func_dump_register(diag_call_stack);
#endif

#endif
